package cn.eud.hunau.service.impl;
import Jama.Matrix;
import Jama.SingularValueDecomposition;
import cn.eud.hunau.mapper.RatingMapper;
import cn.eud.hunau.pojo.UserRateMovie;
import cn.eud.hunau.service.RatingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;


/**
 * @author 陈柳吟
 */
@Service
public class RatingServiceImpl implements RatingService {

    @Autowired
    private RatingMapper ratingMapper;






    @Override
    public List<Long> recommend(Long targetUserId, int topN) {
        List<UserRateMovie> allRatings = ratingMapper.selectAll();

        // 生成user、movie索引映射（userId->rowIndex, movieId->colIndex）
        Set<Long> userSet = new HashSet<>(), movieSet = new HashSet<>();
        for (UserRateMovie r : allRatings) {
            userSet.add(r.getUserId());
            movieSet.add(r.getMovieId());
        }
        List<Long> users = new ArrayList<>(userSet);
        List<Long> movies = new ArrayList<>(movieSet);
        Collections.sort(users);
        Collections.sort(movies);

        Map<Long, Integer> userIdx = new HashMap<>();
        for (int i = 0; i < users.size(); i++) {
            userIdx.put(users.get(i), i);
        }
        Map<Long, Integer> movieIdx = new HashMap<>();
        for (int i = 0; i < movies.size(); i++) {
            movieIdx.put(movies.get(i), i);
        }

        // 1. 构建评分矩阵
        double[][] data = new double[users.size()][movies.size()];
        for (UserRateMovie r : allRatings) {
            data[userIdx.get(r.getUserId())][movieIdx.get(r.getMovieId())] = r.getScore();
        }

        // 2. SVD分解
        Matrix matrix = new Matrix(data);
        SingularValueDecomposition svd = new SingularValueDecomposition(matrix);

        // 3. 计算降维后的用户和物品向量（这里降到20维：取前20列/行）


        int kAll = svd.getSingularValues().length;
        int uRows = svd.getU().getRowDimension();
        int vRows = svd.getV().getRowDimension();
        int k = Math.min(Math.min(Math.min(20, kAll), users.size()), movies.size());
        k = Math.min(Math.min(k, uRows), vRows);

        Matrix u = svd.getU().getMatrix(0, uRows - 1, 0, k - 1);
        Matrix s = Matrix.identity(k, k);
        for (int i = 0; i < k; i++) {
            s.set(i, i, svd.getSingularValues()[i]);
        }
        Matrix v = svd.getV().getMatrix(0, vRows - 1, 0, k - 1);

        // 4. 实时预测user所有未评分电影分数，按分数排序推荐topN
        Integer uidx = userIdx.get(targetUserId);
        if (uidx == null) {
            return new ArrayList<>();
        }
        double[] userFeature = u.getArray()[uidx];
        Set<Integer> ratedMovieIdx = new HashSet<>();
        for (UserRateMovie r:allRatings){

            if (r.getUserId().equals(targetUserId)) {
                ratedMovieIdx.add(movieIdx.get(r.getMovieId()));
            }
        }

        // 用降维特征预测分数
        List<Map.Entry<Integer, Double>> recList = new ArrayList<>();
        for (int m = 0; m < movies.size(); m++) {
            if (ratedMovieIdx.contains(m)) {continue; }
            double[] movieFeature = v.getArray()[m];
            double pred = 0;
            for (int i = 0; i < k; i++) {
                pred += userFeature[i] * s.get(i, i) * movieFeature[i];
            }
            recList.add(Map.entry(m, pred));
        }
        // 按分数降序
        recList.sort((a,b)->Double.compare(b.getValue(), a.getValue()));

        // 返回前topN的movie_id
        List<Long> recMovieIds = new ArrayList<>();
        for (int i = 0; i < Math.min(topN, recList.size()); i++) {
            recMovieIds.add(movies.get(recList.get(i).getKey()));
        }
        return recMovieIds;
    }
}
